﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArch.Utils;
using YJKArch.Utils.Generics;

namespace YJKArch.ColumnModule
{
	public class ColumnSplitWallControler
	{
		public ColumnSplitWallControler(UIApplication uiApplication)
		{
			this.m_uiApplication = uiApplication;
			this.m_doc = this.m_uiApplication.ActiveUIDocument.Document;
		}

		public Result SplitWallByColumn(FamilyInstance column)
		{
			List<Wall> wallByColumnBoundingBox = this.GetWallByColumnBoundingBox(column);
			if (wallByColumnBoundingBox.Count == 0)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			List<EdgeInfo> columnProfile = this.GetColumnProfile(column);
			if (columnProfile.Count == 0)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			double columnElevation = 0.0;
			double columnHeight = 0.0;
			if (!this.GetColumnElevationInfo(column, ref columnElevation, ref columnHeight))
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			foreach (Wall wall in wallByColumnBoundingBox)
			{
				this.SplitSingleWallByColumn(wall, columnProfile, columnElevation, columnHeight);
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private List<EdgeInfo> GetColumnProfile(FamilyInstance column)
		{
			LocationPoint locationPoint = column.Location as LocationPoint;
			List<EdgeInfo> list = new List<EdgeInfo>();
			Transform transform = RevitVersionFuncs.CreateTranslation(locationPoint.Point);
			Transform matrix = RevitVersionFuncs.CreateRotationAtPoint(XYZ.BasisZ, locationPoint.Rotation, locationPoint.Point).Multiply(transform);
			Options options = this.m_uiApplication.Application.Create.NewGeometryOptions();
			GeometryElement geometryElement = column.Symbol.get_Geometry(options);
			if (null == geometryElement)
			{
				return list;
			}
			try
			{
				foreach (GeometryObject geometryObject in geometryElement)
				{
					if (!(geometryObject.GetType() != typeof(Solid)))
					{
						Solid solid = geometryObject as Solid;
						if (solid != null)
						{
							List<Curve> list2 = new List<Curve>();
							this.GetBoundary(solid, ref list2);
							foreach (Curve curve in list2)
							{
								EdgeInfo edgeInfo = this.ConvertCurveToEdgeInfo(curve);
								edgeInfo.TransformSelf(matrix);
								list.Add(edgeInfo);
							}
						}
					}
				}
			}
			catch (Exception)
			{
				list.Clear();
				return list;
			}
			return list;
		}

		private EdgeInfo ConvertCurveToEdgeInfo(Curve curve)
		{
			EdgeInfo result = null;
			XYZ xyz = this.To2D(curve.GetEndPoint(0));
			XYZ xyz2 = this.To2D(curve.GetEndPoint(1));
			Line line = curve as Line;
			if (null != line)
			{
				result = new EdgeInfo(xyz, xyz2, 0.0);
			}
			Arc arc = curve as Arc;
			if (null != arc)
			{
				XYZ centerPt = this.To2D(arc.Center);
				if (!arc.Normal.IsAlmostEqualTo(XYZ.BasisZ))
				{
					XYZ xyz3 = xyz;
					xyz = xyz2;
					xyz2 = xyz3;
				}
				double bulge = Geometry.GetBulge(xyz, xyz2, centerPt, true);
				result = new EdgeInfo(xyz, xyz2, bulge);
			}
			return result;
		}

		private void GetBoundary(Solid solid, ref List<Curve> singleBoundary)
		{
			PlanarFace planarFace = null;
			foreach (object obj in solid.Faces)
			{
				PlanarFace planarFace2 = ((Face)obj) as PlanarFace;
				if (null != planarFace2 && planarFace2.FaceNormal().IsAlmostEqualTo(XYZ.BasisZ) && (null == planarFace || planarFace2.Origin.Z < planarFace.Origin.Z))
				{
					planarFace = planarFace2;
				}
			}
			if (null != planarFace)
			{
				foreach (object obj2 in planarFace.EdgeLoops)
				{
					foreach (object obj3 in ((EdgeArray)obj2))
					{
						Curve curve = ((Edge)obj3).AsCurve();
						if (curve.GetType() == typeof(Line) || curve.GetType() == typeof(Arc))
						{
							singleBoundary.Add(curve);
						}
					}
				}
			}
		}

		private List<Wall> GetWallByColumnBoundingBox(FamilyInstance column)
		{
			List<Wall> list = new List<Wall>();
			try
			{
				Autodesk.Revit.DB.View activeView = this.m_uiApplication.ActiveUIDocument.ActiveView;
				BoundingBoxXYZ box = column.get_BoundingBox(activeView);
				foreach (Element element in new FilteredElementCollector(this.m_doc).OfClass(typeof(Wall)).ToElements())
				{
					Wall wall = element as Wall;
					if (wall.ParametersMap.Contains("已附着顶部") || wall.ParametersMap.Contains("Top is Attached"))
					{
						BoundingBoxXYZ box2 = wall.get_BoundingBox(activeView);
						if (this.IsCrossingWithBoundingBox(box, box2))
						{
							list.Add(wall);
						}
					}
				}
			}
			catch (Exception)
			{
				return list;
			}
			return list;
		}

		private bool IsCrossingWithBoundingBox(BoundingBoxXYZ box1, BoundingBoxXYZ box2)
		{
			double x = box1.Min.X;
			double y = box1.Min.Y;
			double z = box1.Min.Z;
			double x2 = box1.Max.X;
			double y2 = box1.Max.Y;
			double z2 = box1.Max.Z;
			double x3 = box2.Min.X;
			double y3 = box2.Min.Y;
			double z3 = box2.Min.Z;
			double x4 = box2.Max.X;
			double y4 = box2.Max.Y;
			double z4 = box2.Max.Z;
			return !Geometry.Lessthan_Or_Equal(x2, x3) && !Geometry.Greaterthan_Or_Equal(x, x4) && !Geometry.Lessthan_Or_Equal(y2, y3) && !Geometry.Greaterthan_Or_Equal(y, y4) && !Geometry.Lessthan_Or_Equal(z2, z3) && !Geometry.Greaterthan_Or_Equal(z, z4);
		}

		private void SplitSingleWallByColumn(Wall wall, List<EdgeInfo> columnProfile, double columnElevation, double columnHeight)
		{
			try
			{
				EdgeInfo centerLine = null;
				EdgeInfo edgeInfo = null;
				EdgeInfo edgeInfo2 = null;
				this.GetWallLine(wall, ref centerLine, ref edgeInfo, ref edgeInfo2);
				List<XYZ> sideLineIntersectPoints = this.GetSideLineIntersectPoints(edgeInfo, columnProfile);
				List<XYZ> sideLineIntersectPoints2 = this.GetSideLineIntersectPoints(edgeInfo2, columnProfile);
				this.SortPoints(centerLine, edgeInfo, edgeInfo2, ref sideLineIntersectPoints, ref sideLineIntersectPoints2);
				XYZ ptStart = null;
				XYZ ptEnd = null;
				if (sideLineIntersectPoints.Count > 1 && sideLineIntersectPoints2.Count > 1)
				{
					if (!this.OperateDoubleSide(centerLine, edgeInfo, edgeInfo2, sideLineIntersectPoints, sideLineIntersectPoints2, ref ptStart, ref ptEnd))
					{
						return;
					}
				}
				else if (sideLineIntersectPoints.Count > 1)
				{
					this.OperateSingleSide(centerLine, sideLineIntersectPoints, ref ptStart, ref ptEnd);
				}
				else if (sideLineIntersectPoints2.Count > 1)
				{
					this.OperateSingleSide(centerLine, sideLineIntersectPoints2, ref ptStart, ref ptEnd);
				}
				else if (!this.OperateCenterLine(centerLine, columnProfile, ref ptStart, ref ptEnd))
				{
					return;
				}
				this.CreateWallOpening(wall, ptStart, ptEnd, columnElevation, columnHeight);
			}
			catch (Exception)
			{
			}
		}

		private XYZ To2D(XYZ pt)
		{
			return new XYZ(pt.X, pt.Y, 0.0);
		}

		private void GetWallLine(Wall wall, ref EdgeInfo centerLine, ref EdgeInfo positiveLine, ref EdgeInfo negativeLine)
		{
			double num = wall.Width / 2.0;
			Curve curve = (wall.Location as LocationCurve).Curve;
			XYZ xyz = this.To2D(curve.GetEndPoint(0));
			XYZ xyz2 = this.To2D(curve.GetEndPoint(1));
			Line line = curve as Line;
			if (null != line)
			{
				centerLine = new EdgeInfo(xyz, xyz2, 0.0);
				XYZ xyz3 = Geometry.RotateTo((xyz2 - xyz).Normalize(), -Math.PI*.5, XYZ.BasisZ);
				XYZ xyz4 = xyz3.Negate();
				xyz3 *= num;
				xyz4 *= num;
				XYZ startPoint = xyz + xyz3;
				XYZ endPoint = xyz2 + xyz3;
				XYZ startPoint2 = xyz + xyz4;
				XYZ endPoint2 = xyz2 + xyz4;
				positiveLine = new EdgeInfo(startPoint, endPoint, 0.0);
				negativeLine = new EdgeInfo(startPoint2, endPoint2, 0.0);
			}
			Arc arc = curve as Arc;
			if (null != arc)
			{
				XYZ xyz5 = this.To2D(arc.Center);
				if (!arc.Normal.IsAlmostEqualTo(XYZ.BasisZ))
				{
					XYZ xyz6 = xyz;
					xyz = xyz2;
					xyz2 = xyz6;
				}
				double bulge = Geometry.GetBulge(xyz, xyz2, xyz5, true);
				centerLine = new EdgeInfo(xyz, xyz2, bulge);
				double num2 = xyz5.DistanceTo(xyz);
				XYZ xyz7 = (xyz - xyz5).Normalize() * (num2 + num);
				XYZ xyz8 = (xyz2 - xyz5).Normalize() * (num2 + num);
				XYZ xyz9 = (xyz - xyz5).Normalize() * (num2 - num);
				XYZ xyz10 = (xyz2 - xyz5).Normalize() * (num2 - num);
				XYZ startPoint3 = xyz5 + xyz7;
				XYZ endPoint3 = xyz5 + xyz8;
				XYZ startPoint4 = xyz5 + xyz9;
				XYZ endPoint4 = xyz5 + xyz10;
				positiveLine = new EdgeInfo(startPoint3, endPoint3, bulge);
				negativeLine = new EdgeInfo(startPoint4, endPoint4, bulge);
			}
		}

		private List<XYZ> GetSideLineIntersectPoints(EdgeInfo wallSideLine, List<EdgeInfo> columnProfile)
		{
			Curve curve = this.ConvertEdgeInfoToCurve(wallSideLine);
			List<XYZ> list = new List<XYZ>();
			foreach (EdgeInfo edge in columnProfile)
			{
				Curve curve2 = this.ConvertEdgeInfoToCurve(edge);
				IntersectionResultArray intersectionResultArray;
				if (curve.Intersect(curve2, out intersectionResultArray) == SetComparisonResult.Overlap)
				{
					foreach (object obj in intersectionResultArray)
					{
						IntersectionResult intersectionResult = (IntersectionResult)obj;
						XYZ ptInter = intersectionResult.XYZPoint;
						if (list.FindIndex((XYZ value) => value.IsAlmostEqualTo(ptInter)) == -1)
						{
							list.Add(ptInter);
						}
					}
				}
			}
			return list;
		}

		private Curve ConvertEdgeInfoToCurve(EdgeInfo edge)
		{
			if (edge.IsArc)
			{
				XYZ radianPnt = Geometry.CalculatMidPoint(edge.StartPoint, edge.EndPoint, edge.Center, XYZ.BasisZ);
				return this.m_uiApplication.Application.CreatYJKArc(edge.StartPoint, edge.EndPoint, radianPnt);
			}
			return YJKLineEx.YJKGetBound(edge.StartPoint, edge.EndPoint);
		}

		private bool CreateWallOpening(Wall wall, XYZ ptStart, XYZ ptEnd, double columnElevation, double columnHeight)
		{
			double num = 0.0;
			double num2 = 0.0;
			this.GetWallElevationInfo(wall, ref num, ref num2);
			double num3 = columnElevation;
			if (Geometry.LessThan(columnElevation, num))
			{
				num3 = num;
			}
			ptStart += XYZ.BasisZ * num3;
			double num4 = columnElevation + columnHeight;
			if (Geometry.GreaterThan(columnElevation + columnHeight, num + num2))
			{
				num4 = num + num2;
			}
			ptEnd += XYZ.BasisZ * num4;
			bool result;
			try
			{
				this.m_doc.Create.NewOpening(wall, ptStart, ptEnd);
				result = true;
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				result = false;
			}
			return result;
		}

		private bool GetColumnElevationInfo(FamilyInstance column, ref double columnElevation, ref double columnHeight)
		{
			bool result;
			try
			{
				ElementId elementId = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).AsElementId();
				double elevation = (this.m_doc.GetElement(elementId) as Level).Elevation;
				double num = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).AsDouble();
				columnElevation = elevation + num;
				ElementId elementId2 = column.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).AsElementId();
				double elevation2 = (this.m_doc.GetElement(elementId2) as Level).Elevation;
				double num2 = column.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).AsDouble();
				double num3 = elevation2 + num2;
				columnHeight = num3 - columnElevation;
				result = true;
			}
			catch (Exception)
			{
				result = false;
			}
			return result;
		}

		private void GetWallElevationInfo(Wall wall, ref double wallElevation, ref double wallHeight)
		{
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			double elevation = (this.m_doc.GetElement(elementId) as Level).Elevation;
			double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
			wallElevation = elevation + num;
			ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId2 == ElementId.InvalidElementId)
			{
				double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
				wallHeight = num2;
				return;
			}
			Level level = this.m_doc.GetElement(elementId2) as Level;
			double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
			wallHeight = level.Elevation + num3 - wallElevation;
		}

		private void SortPoints(EdgeInfo centerLine, EdgeInfo positiveLine, EdgeInfo negativeLine, ref List<XYZ> interPtsPositive, ref List<XYZ> interPtsNegative)
		{
			if (centerLine.IsArc)
			{
				interPtsPositive.Sort(new AngleComparer(positiveLine.StartPoint, positiveLine.Center));
				interPtsNegative.Sort(new AngleComparer(negativeLine.StartPoint, negativeLine.Center));
				return;
			}
			interPtsPositive.Sort(new DistanceComparer(positiveLine.StartPoint));
			interPtsNegative.Sort(new DistanceComparer(negativeLine.StartPoint));
		}

		private bool OperateDoubleSide(EdgeInfo centerLine, EdgeInfo positiveLine, EdgeInfo negativeLine, List<XYZ> interPtsPositive, List<XYZ> interPtsNegative, ref XYZ ptStart, ref XYZ ptEnd)
		{
			XYZ startPoint = centerLine.StartPoint;
			XYZ endPoint = centerLine.EndPoint;
			if (centerLine.IsArc)
			{
				XYZ center = centerLine.Center;
				XYZ ptS = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPtsPositive[0]);
				XYZ ptE = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPtsPositive[interPtsPositive.Count - 1]);
				XYZ ptS2 = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPtsNegative[0]);
				XYZ ptE2 = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPtsNegative[interPtsNegative.Count - 1]);
				Geometry.OverlapType overlapType = Geometry.IsOverlapOfTwoArc(ptS, ptE, center, ptS2, ptE2, center, ref ptStart, ref ptEnd, 1E-09);
				if (overlapType == Geometry.OverlapType._OT_Linked || overlapType == Geometry.OverlapType._OT_None)
				{
					return false;
				}
			}
			else
			{
				XYZ ptS3 = Geometry.CalculateFootPoint(startPoint, endPoint, interPtsPositive[0]);
				XYZ ptE3 = Geometry.CalculateFootPoint(startPoint, endPoint, interPtsPositive[interPtsPositive.Count - 1]);
				XYZ ptS4 = Geometry.CalculateFootPoint(startPoint, endPoint, interPtsNegative[0]);
				XYZ ptE4 = Geometry.CalculateFootPoint(startPoint, endPoint, interPtsNegative[interPtsNegative.Count - 1]);
				Geometry.OverlapType overlapType2 = Geometry.IsOverlapOfTwoLine(ptS3, ptE3, ptS4, ptE4, ref ptStart, ref ptEnd, true, 1E-09);
				if (overlapType2 == Geometry.OverlapType._OT_Linked || overlapType2 == Geometry.OverlapType._OT_None)
				{
					return false;
				}
			}
			return true;
		}

		private void OperateSingleSide(EdgeInfo centerLine, List<XYZ> interPts, ref XYZ ptStart, ref XYZ ptEnd)
		{
			XYZ startPoint = centerLine.StartPoint;
			XYZ endPoint = centerLine.EndPoint;
			if (centerLine.IsArc)
			{
				XYZ center = centerLine.Center;
				ptStart = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPts[0]);
				ptEnd = Geometry.CalculateFootPoint(startPoint, endPoint, center, interPts[interPts.Count - 1]);
				return;
			}
			ptStart = Geometry.CalculateFootPoint(startPoint, endPoint, interPts[0]);
			ptEnd = Geometry.CalculateFootPoint(startPoint, endPoint, interPts[interPts.Count - 1]);
		}

		private void AddPoint(XYZ pt, ref List<XYZ> points)
		{
			if (points.FindIndex((XYZ value) => value.IsAlmostEqualTo(pt)) == -1)
			{
				points.Add(pt);
			}
		}

		private void AddPoint(List<XYZ> pts, ref List<XYZ> points)
		{
			foreach (XYZ pt in pts)
			{
				this.AddPoint(pt, ref points);
			}
		}

		private bool OperateCenterLine(EdgeInfo centerLine, List<EdgeInfo> columnProfile, ref XYZ ptStart, ref XYZ ptEnd)
		{
			List<XYZ> list = new List<XYZ>();
			XYZ startPoint = centerLine.StartPoint;
			XYZ endPoint = centerLine.EndPoint;
			if (centerLine.IsArc)
			{
				XYZ center = centerLine.Center;
				foreach (EdgeInfo edgeInfo in columnProfile)
				{
					if (edgeInfo.IsArc)
					{
						this.AddPoint(this.GetSideLineIntersectPoints(centerLine, new List<EdgeInfo>
						{
							edgeInfo
						}), ref list);
					}
					else
					{
						this.AddPoint(Geometry.CalculateFootPoint(startPoint, endPoint, center, edgeInfo.StartPoint), ref list);
					}
				}
				list.Sort(new AngleComparer(startPoint, center));
			}
			else
			{
				foreach (EdgeInfo edgeInfo2 in columnProfile)
				{
					if (edgeInfo2.IsArc)
					{
						this.AddPoint(this.GetSideLineIntersectPoints(centerLine, new List<EdgeInfo>
						{
							edgeInfo2
						}), ref list);
					}
					else
					{
						this.AddPoint(Geometry.CalculateFootPoint(startPoint, endPoint, edgeInfo2.StartPoint), ref list);
					}
				}
				list.Sort(new DistanceComparer(startPoint));
			}
			if (list.Count < 1)
			{
				return false;
			}
			if (list.Count < 2)
			{
				XYZ xyz = list[0];
				if (Geometry.LessThan(xyz.DistanceTo(startPoint), xyz.DistanceTo(endPoint)))
				{
					list.Add(startPoint);
				}
				else
				{
					list.Add(endPoint);
				}
			}
			ptStart = list[0];
			ptEnd = list[list.Count - 1];
			return true;
		}

		public void ColumnJoinWall(Element column, StructuralType sType)
		{
			foreach (Element wall in this.GetWallsIntersectElement(column))
			{
				this.JoinTwoElements(column, wall, sType);
			}
		}

		private List<Element> GetWallsIntersectElement(Element column)
		{
			List<Element> list = new List<Element>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			filteredElementCollector.OfClass(typeof(Wall));
			ElementIntersectsElementFilter elementIntersectsElementFilter = new ElementIntersectsElementFilter(column);
			filteredElementCollector.WherePasses(elementIntersectsElementFilter);
			foreach (Element item in filteredElementCollector)
			{
				list.Add(item);
			}
			return list;
		}

		private void JoinTwoElements(Element column, Element wall, StructuralType sType)
		{
			if (!JoinGeometryUtils.AreElementsJoined(this.m_doc, column, wall))
			{
				JoinGeometryUtils.JoinGeometry(this.m_doc, column, wall);
				this.m_doc.Regenerate();
			}
			this.SetJoinOrder(column, wall, sType);
		}

		private void SetJoinOrder(Element column, Element wall, StructuralType sType)
		{
            if ((int)sType == 3)
			{
				if (JoinGeometryUtils.IsCuttingElementInJoin(this.m_doc, wall, column))
				{
					JoinGeometryUtils.SwitchJoinOrder(this.m_doc, column, wall);
					this.m_doc.Regenerate();
					return;
				}
			}
			else if (JoinGeometryUtils.IsCuttingElementInJoin(this.m_doc, column, wall))
			{
				JoinGeometryUtils.SwitchJoinOrder(this.m_doc, wall, column);
				this.m_doc.Regenerate();
			}
		}

		private UIApplication m_uiApplication;

		private Autodesk.Revit.DB.Document m_doc;
	}
}
